<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Spine-Leaf网络监控拓扑</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Arial, sans-serif;
        }

        body {
            background: linear-gradient(135deg, #0c1a2d 0%, #152642 100%);
            color: #e0e0e0;
            min-height: 100vh;
            overflow-x: hidden;
            padding: 20px;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 15px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
        }

        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .subtitle {
            font-size: 1.2rem;
            color: #8a9ba8;
        }

        .dashboard {
            display: grid;
            grid-template-columns: 1fr 300px;
            gap: 20px;
        }

        .topology-container {
            background: rgba(16, 29, 47, 0.8);
            border-radius: 12px;
            padding: 25px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
            position: relative;
            min-height: 700px;
            overflow: hidden;
        }

        .stats-panel {
            background: rgba(16, 29, 47, 0.8);
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
            display: flex;
            flex-direction: column;
            gap: 20px;
        }

        .panel-title {
            font-size: 1.3rem;
            margin-bottom: 15px;
            color: #4facfe;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
            padding-bottom: 10px;
        }

        .stat-card {
            background: rgba(30, 41, 59, 0.7);
            border-radius: 8px;
            padding: 15px;
            border-left: 4px solid #4facfe;
        }

        .stat-title {
            font-size: 0.9rem;
            color: #8a9ba8;
            margin-bottom: 8px;
        }

        .stat-value {
            font-size: 1.8rem;
            font-weight: bold;
            color: #4facfe;
        }

        /* 拓扑图样式 */
        .topology {
            position: relative;
            height: 650px;
        }

        .spine-layer {
            display: flex;
            justify-content: space-around;
            margin-bottom: 100px;
        }

        .leaf-layer {
            display: flex;
            justify-content: space-around;
            margin-bottom: 100px;
        }

        .server-layer {
            display: flex;
            justify-content: space-around;
        }

        .device {
            position: relative;
            display: flex;
            flex-direction: column;
            align-items: center;
            cursor: pointer;
            transition: transform 0.3s;
            z-index: 10;
        }

        .device:hover {
            transform: translateY(-5px);
        }

        .device-icon {
            width: 80px;
            height: 80px;
            border-radius: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.8rem;
            margin-bottom: 10px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
            transition: all 0.3s;
        }

        .spine .device-icon {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        }

        .leaf .device-icon {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
        }

        .server .device-icon {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
        }

        .device-label {
            font-size: 0.9rem;
            color: #e0e0e0;
            text-align: center;
            background: rgba(0, 0, 0, 0.5);
            padding: 4px 10px;
            border-radius: 4px;
        }

        .device-status {
            position: absolute;
            top: -5px;
            right: -5px;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: #4ade80;
            border: 2px solid #152642;
        }

        .device-status.warning {
            background: #f59e0b;
        }

        .device-status.error {
            background: #ef4444;
        }

        /* 连接线样式 */
        .connections {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
        }

        .connection {
            position: absolute;
            background: rgba(79, 172, 254, 0.7);
            height: 3px;
            transform-origin: 0 0;
            z-index: 1;
        }

        .connection::after {
            content: '';
            position: absolute;
            right: -6px;
            top: -3px;
            width: 0;
            height: 0;
            border-left: 6px solid rgba(79, 172, 254, 0.7);
            border-top: 3px solid transparent;
            border-bottom: 3px solid transparent;
        }

        .traffic-value {
            position: absolute;
            font-size: 0.8rem;
            background: rgba(0, 0, 0, 0.7);
            padding: 2px 6px;
            border-radius: 4px;
            color: #4ade80;
            transform: translate(-50%, -50%);
        }

        /* 设备详情弹窗 */
        .device-detail {
            position: absolute;
            background: rgba(16, 29, 47, 0.95);
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
            width: 300px;
            z-index: 100;
            display: none;
            border: 1px solid rgba(79, 172, 254, 0.3);
        }

        .device-detail h3 {
            margin-bottom: 15px;
            color: #4facfe;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
            padding-bottom: 8px;
        }

        .detail-item {
            display: flex;
            justify-content: space-between;
            margin-bottom: 10px;
            padding-bottom: 8px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.05);
        }

        .detail-label {
            color: #8a9ba8;
        }

        .detail-value {
            color: #e0e0e0;
            font-weight: 500;
        }

        .progress-bar {
            height: 8px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 4px;
            margin-top: 5px;
            overflow: hidden;
        }

        .progress-fill {
            height: 100%;
            border-radius: 4px;
            background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
        }

        .last-updated {
            text-align: center;
            margin-top: 20px;
            font-size: 0.8rem;
            color: #8a9ba8;
        }

        .alert-list {
            margin-top: 20px;
        }

        .alert-item {
            background: rgba(239, 68, 68, 0.2);
            border-left: 4px solid #ef4444;
            padding: 10px;
            margin-bottom: 10px;
            border-radius: 4px;
            font-size: 0.9rem;
        }

        @media (max-width: 1200px) {
            .dashboard {
                grid-template-columns: 1fr;
            }

            .stats-panel {
                order: -1;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>Spine-Leaf网络监控拓扑</h1>
            <p class="subtitle">实时监控网络设备状态与流量</p>
        </header>

        <div class="dashboard">
            <div class="topology-container">
                <div class="topology">
                    <!-- Spine层 -->
                    <div class="spine-layer">
                        <div class="device spine" data-id="spine-1">
                            <div class="device-icon">
                                <i>🔗</i>
                            </div>
                            <div class="device-label">Spine-1</div>
                            <div class="device-status"></div>
                        </div>
                        <div class="device spine" data-id="spine-2">
                            <div class="device-icon">
                                <i>🔗</i>
                            </div>
                            <div class="device-label">Spine-2</div>
                            <div class="device-status"></div>
                        </div>
                    </div>

                    <!-- Leaf层 -->
                    <div class="leaf-layer">
                        <div class="device leaf" data-id="leaf-1">
                            <div class="device-icon">
                                <i>🔀</i>
                            </div>
                            <div class="device-label">Leaf-1</div>
                            <div class="device-status"></div>
                        </div>
                        <div class="device leaf" data-id="leaf-2">
                            <div class="device-icon">
                                <i>🔀</i>
                            </div>
                            <div class="device-label">Leaf-2</div>
                            <div class="device-status"></div>
                        </div>
                        <div class="device leaf" data-id="leaf-3">
                            <div class="device-icon">
                                <i>🔀</i>
                            </div>
                            <div class="device-label">Leaf-3</div>
                            <div class="device-status"></div>
                        </div>
                        <div class="device leaf" data-id="leaf-4">
                            <div class="device-icon">
                                <i>🔀</i>
                            </div>
                            <div class="device-label">Leaf-4</div>
                            <div class="device-status"></div>
                        </div>
                    </div>

                    <!-- 服务器层 -->
                    <div class="server-layer">
                        <div class="device server" data-id="server-1">
                            <div class="device-icon">
                                <i>🖥️</i>
                            </div>
                            <div class="device-label">Server-1</div>
                            <div class="device-status"></div>
                        </div>
                        <div class="device server" data-id="server-2">
                            <div class="device-icon">
                                <i>🖥️</i>
                            </div>
                            <div class="device-label">Server-2</div>
                            <div class="device-status"></div>
                        </div>
                        <div class="device server" data-id="server-3">
                            <div class="device-icon">
                                <i>🖥️</i>
                            </div>
                            <div class="device-label">Server-3</div>
                            <div class="device-status"></div>
                        </div>
                        <div class="device server" data-id="server-4">
                            <div class="device-icon">
                                <i>🖥️</i>
                            </div>
                            <div class="device-label">Server-4</div>
                            <div class="device-status"></div>
                        </div>
                    </div>

                    <!-- 连接线和流量 -->
                    <div class="connections" id="connections"></div>

                    <!-- 设备详情弹窗 -->
                    <div class="device-detail" id="deviceDetail"></div>
                </div>
            </div>

            <div class="stats-panel">
                <h2 class="panel-title">网络概览</h2>

                <div class="stat-card">
                    <div class="stat-title">总设备数</div>
                    <div class="stat-value" id="totalDevices">10</div>
                </div>

                <div class="stat-card">
                    <div class="stat-title">在线设备</div>
                    <div class="stat-value" id="onlineDevices">10</div>
                </div>

                <div class="stat-card">
                    <div class="stat-title">网络流量</div>
                    <div class="stat-value" id="totalTraffic">1.24 Gbps</div>
                </div>

                <div class="stat-card">
                    <div class="stat-title">CPU平均使用率</div>
                    <div class="stat-value" id="avgCpu">42%</div>
                </div>

                <div class="stat-card">
                    <div class="stat-title">内存平均使用率</div>
                    <div class="stat-value" id="avgMemory">58%</div>
                </div>

                <div class="alert-list">
                    <h2 class="panel-title">告警信息</h2>
                    <div class="alert-item">
                        Leaf-3 端口 eth0/3 流量异常
                    </div>
                    <div class="alert-item">
                        Server-2 CPU使用率超过阈值
                    </div>
                </div>

                <div class="last-updated">
                    最后更新: <span id="lastUpdateTime">--:--:--</span>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 设备位置和连接关系
        const devicePositions = {};
        const connections = [
            // Spine到Leaf的连接
            { from: 'spine-1', to: 'leaf-1', id: 's1-l1' },
            { from: 'spine-1', to: 'leaf-2', id: 's1-l2' },
            { from: 'spine-1', to: 'leaf-3', id: 's1-l3' },
            { from: 'spine-1', to: 'leaf-4', id: 's1-l4' },
            { from: 'spine-2', to: 'leaf-1', id: 's2-l1' },
            { from: 'spine-2', to: 'leaf-2', id: 's2-l2' },
            { from: 'spine-2', to: 'leaf-3', id: 's2-l3' },
            { from: 'spine-2', to: 'leaf-4', id: 's2-l4' },

            // Leaf到Server的连接
            { from: 'leaf-1', to: 'server-1', id: 'l1-sv1' },
            { from: 'leaf-2', to: 'server-2', id: 'l2-sv2' },
            { from: 'leaf-3', to: 'server-3', id: 'l3-sv3' },
            { from: 'leaf-4', to: 'server-4', id: 'l4-sv4' }
        ];

        // 初始化设备位置
        function initDevicePositions() {
            document.querySelectorAll('.device').forEach(device => {
                const rect = device.getBoundingClientRect();
                const containerRect = document.querySelector('.topology').getBoundingClientRect();

                devicePositions[device.dataset.id] = {
                    x: rect.left + rect.width/2 - containerRect.left,
                    y: rect.top + rect.height/2 - containerRect.top
                };
            });
        }

        // 绘制连接线
        function drawConnections() {
            const connectionsContainer = document.getElementById('connections');
            connectionsContainer.innerHTML = '';

            connections.forEach(conn => {
                const fromPos = devicePositions[conn.from];
                const toPos = devicePositions[conn.to];

                if (!fromPos || !toPos) return;

                // 计算连接线长度和角度
                const dx = toPos.x - fromPos.x;
                const dy = toPos.y - fromPos.y;
                const length = Math.sqrt(dx * dx + dy * dy);
                const angle = Math.atan2(dy, dx) * 180 / Math.PI;

                // 创建连接线
                const connection = document.createElement('div');
                connection.className = 'connection';
                connection.id = `conn-${conn.id}`;
                connection.style.width = `${length}px`;
                connection.style.left = `${fromPos.x}px`;
                connection.style.top = `${fromPos.y}px`;
                connection.style.transform = `rotate(${angle}deg)`;

                connectionsContainer.appendChild(connection);

                // 添加流量显示
                const trafficValue = document.createElement('div');
                trafficValue.className = 'traffic-value';
                trafficValue.id = `traffic-${conn.id}`;
                trafficValue.style.left = `${fromPos.x + dx/2}px`;
                trafficValue.style.top = `${fromPos.y + dy/2}px`;
                trafficValue.textContent = '0 Mbps';

                connectionsContainer.appendChild(trafficValue);
            });
        }

        // 更新设备状态
        function updateDeviceStatus(deviceId, status) {
            const device = document.querySelector(`[data-id="${deviceId}"] .device-status`);
            if (device) {
                device.className = 'device-status';
                if (status === 'warning') {
                    device.classList.add('warning');
                } else if (status === 'error') {
                    device.classList.add('error');
                }
            }
        }

        // 显示设备详情
        function showDeviceDetail(deviceId, x, y) {
            const detailPanel = document.getElementById('deviceDetail');
            const deviceData = mockDeviceData[deviceId];

            if (!deviceData) return;

            let detailHTML = `<h3>${deviceData.name}</h3>`;

            // 添加设备详情
            detailHTML += `
                <div class="detail-item">
                    <span class="detail-label">状态:</span>
                    <span class="detail-value">${deviceData.status}</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">IP地址:</span>
                    <span class="detail-value">${deviceData.ip}</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">型号:</span>
                    <span class="detail-value">${deviceData.model}</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">CPU使用率:</span>
                    <span class="detail-value">${deviceData.cpu}%</span>
                </div>
                <div class="progress-bar">
                    <div class="progress-fill" style="width: ${deviceData.cpu}%"></div>
                </div>
                <div class="detail-item">
                    <span class="detail-label">内存使用率:</span>
                    <span class="detail-value">${deviceData.memory}%</span>
                </div>
                <div class="progress-bar">
                    <div class="progress-fill" style="width: ${deviceData.memory}%"></div>
                </div>
                <div class="detail-item">
                    <span class="detail-label">温度:</span>
                    <span class="detail-value">${deviceData.temperature}°C</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">上行流量:</span>
                    <span class="detail-value">${deviceData.txTraffic} Mbps</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">下行流量:</span>
                    <span class="detail-value">${deviceData.rxTraffic} Mbps</span>
                </div>
            `;

            detailPanel.innerHTML = detailHTML;
            detailPanel.style.display = 'block';
            detailPanel.style.left = `${x - 100}px`;
            detailPanel.style.top = `${y - 0}px`;
        }

        // 隐藏设备详情
        function hideDeviceDetail() {
            document.getElementById('deviceDetail').style.display = 'none';
        }

        // 更新连接流量
        function updateConnectionTraffic(connId, traffic) {
            const trafficElement = document.getElementById(`traffic-${connId}`);
            if (trafficElement) {
                trafficElement.textContent = `${traffic} Mbps`;
            }
        }

        // 模拟设备数据
        const mockDeviceData = {
            'spine-1': {
                name: 'Spine-1',
                status: '正常',
                ip: '192.168.1.1',
                model: 'Cisco Nexus 9336C',
                cpu: 32,
                memory: 45,
                temperature: 38,
                txTraffic: 520,
                rxTraffic: 480
            },
            'spine-2': {
                name: 'Spine-2',
                status: '正常',
                ip: '192.168.1.2',
                model: 'Cisco Nexus 9336C',
                cpu: 28,
                memory: 42,
                temperature: 36,
                txTraffic: 480,
                rxTraffic: 510
            },
            'leaf-1': {
                name: 'Leaf-1',
                status: '正常',
                ip: '192.168.1.11',
                model: 'Cisco Nexus 93180',
                cpu: 45,
                memory: 52,
                temperature: 42,
                txTraffic: 320,
                rxTraffic: 290
            },
            'leaf-2': {
                name: 'Leaf-2',
                status: '正常',
                ip: '192.168.1.12',
                model: 'Cisco Nexus 93180',
                cpu: 38,
                memory: 48,
                temperature: 40,
                txTraffic: 280,
                rxTraffic: 310
            },
            'leaf-3': {
                name: 'Leaf-3',
                status: '警告',
                ip: '192.168.1.13',
                model: 'Cisco Nexus 93180',
                cpu: 65,
                memory: 72,
                temperature: 48,
                txTraffic: 410,
                rxTraffic: 390
            },
            'leaf-4': {
                name: 'Leaf-4',
                status: '正常',
                ip: '192.168.1.14',
                model: 'Cisco Nexus 93180',
                cpu: 42,
                memory: 55,
                temperature: 41,
                txTraffic: 290,
                rxTraffic: 270
            },
            'server-1': {
                name: 'Server-1',
                status: '正常',
                ip: '192.168.2.101',
                model: 'Dell PowerEdge R740',
                cpu: 28,
                memory: 62,
                temperature: 35,
                txTraffic: 120,
                rxTraffic: 95
            },
            'server-2': {
                name: 'Server-2',
                status: '警告',
                ip: '192.168.2.102',
                model: 'Dell PowerEdge R740',
                cpu: 85,
                memory: 78,
                temperature: 42,
                txTraffic: 210,
                rxTraffic: 180
            },
            'server-3': {
                name: 'Server-3',
                status: '正常',
                ip: '192.168.2.103',
                model: 'Dell PowerEdge R740',
                cpu: 32,
                memory: 58,
                temperature: 36,
                txTraffic: 95,
                rxTraffic: 110
            },
            'server-4': {
                name: 'Server-4',
                status: '正常',
                ip: '192.168.2.104',
                model: 'Dell PowerEdge R740',
                cpu: 38,
                memory: 65,
                temperature: 38,
                txTraffic: 150,
                rxTraffic: 130
            }
        };

        // 模拟连接流量数据
        function generateTrafficData() {
            connections.forEach(conn => {
                const traffic = Math.floor(Math.random() * 500) + 50;
                updateConnectionTraffic(conn.id, traffic);
            });
        }

        // 更新全局统计
        function updateGlobalStats() {
            let totalCpu = 0;
            let totalMemory = 0;
            let totalTraffic = 0;
            let onlineCount = 0;

            Object.values(mockDeviceData).forEach(device => {
                totalCpu += device.cpu;
                totalMemory += device.memory;
                totalTraffic += device.txTraffic + device.rxTraffic;
                onlineCount++;
            });

            document.getElementById('totalDevices').textContent = Object.keys(mockDeviceData).length;
            document.getElementById('onlineDevices').textContent = onlineCount;
            document.getElementById('avgCpu').textContent = Math.round(totalCpu / Object.keys(mockDeviceData).length) + '%';
            document.getElementById('avgMemory').textContent = Math.round(totalMemory / Object.keys(mockDeviceData).length) + '%';
            document.getElementById('totalTraffic').textContent = (totalTraffic / 1000).toFixed(2) + ' Gbps';

            // 更新最后更新时间
            const now = new Date();
            document.getElementById('lastUpdateTime').textContent =
                `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
        }

        // 初始化
        function init() {
            // 初始化设备位置和连接
            initDevicePositions();
            drawConnections();

            // 设置设备状态
            updateDeviceStatus('leaf-3', 'warning');
            updateDeviceStatus('server-2', 'warning');

            // 设置设备鼠标事件
            document.querySelectorAll('.device').forEach(device => {
                device.addEventListener('mouseenter', (e) => {
                    showDeviceDetail(device.dataset.id, e.pageX, e.pageY);
                });

                device.addEventListener('mouseleave', () => {
                    hideDeviceDetail();
                });

                device.addEventListener('mousemove', (e) => {
                    const detailPanel = document.getElementById('deviceDetail');
                    if (detailPanel.style.display === 'block') {
                        detailPanel.style.left = `${e.pageX + 20}px`;
                        detailPanel.style.top = `${e.pageY - 100}px`;
                    }
                });
            });

            // 生成初始流量数据
            generateTrafficData();
            updateGlobalStats();

            // 定时更新数据
            setInterval(() => {
                generateTrafficData();
                updateGlobalStats();
            }, 5000);
        }

        // 页面加载完成后初始化
        window.addEventListener('load', init);
        window.addEventListener('resize', () => {
            initDevicePositions();
            drawConnections();
        });
    </script>
</body>
</html>